/*
 * Copyright 2011 Fabian Wenzelmann
 * 
 * This file is part of Recognition-Procedures-for-Boolean-Functions.
 * 
 * Recognition-Procedures-for-Boolean-Functions is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Recognition-Procedures-for-Boolean-Functions is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Recognition-Procedures-for-Boolean-Functions.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.wenzelmf.booleanFunction.lpb.combinatorial.occurrencePattern;

import java.io.Serializable;
import java.util.Iterator;

/**
 * This is a special class of occurrence patterns that use OccurrencePatternEntry objects to store
 * the occurrences. The implementation overrides some methods to be more efficient.
 * 
 * It defines a new abstract method to iterate over all those entries, but implements the Integer
 * iterator from the superclass.
 * 
 * @author Fabian Wenzelmann
 * @version 1.0
 *
 */
public abstract class EntryOccurrencePattern extends OccurrencePattern implements Serializable
{

	private static final long serialVersionUID = -6558363253491929691L;
	
	/**
	 * Constructs new occurrence pattern.
	 * 
	 * @param variable The name of the variable the occurrence pattern is created for (identifier of the variable).
	 */
	public EntryOccurrencePattern(String variable)
	{
		super(variable);
	}
	
	/**
	 * Returns a Iterator over all entries in this pattern (again in ascending order according the
	 * clause size).
	 * 
	 * It may prohibit element removal by throwing an UnsupportedOperationException when the
	 * remove method is called.
	 * 
	 * @return An Iterator over all entries (ascending ordered).
	 */
	public abstract Iterator<OccurrencePatternEntry> entryIterator();
	
	// TODO: maybe speedup?
	@Override
	public int compareTo(OccurrencePattern other)
	{
		return super.compareTo(other);
	}
	
	/**
	 * Behaves as defined by OccurrencePattern class, the Iterator prohibits removing
	 * elements (i.e. the {@code remove} method throws an UnsupportedOperationException).
	 * 
	 * @return An Iterator over all elements in this occurrence pattern as defined in
	 * {@link OccurrencePattern}.
	 */
	@Override
	public Iterator<Integer> iterator()
	{
		return new Iterator<Integer>()
		{
			Iterator<OccurrencePatternEntry> entries = entryIterator();
			int countLeft = 0;
			OccurrencePatternEntry current = null;
			
			@Override
			public boolean hasNext()
			{
				if(countLeft == 0)
					return entries.hasNext();
				else
					return true;
			}

			@Override
			public Integer next()
			{
				if(countLeft == 0)
				{
					current = entries.next();
					countLeft = current.getOccurrences();
				}
				countLeft--;
				return current.getNumber();
			}

			@Override
			public void remove()
			{
				throw new UnsupportedOperationException("Removing elements from the Occurrence Pattern is not allowed");
			}
			
		};
	}
	
	/**
	 * Behaves like {@link OccurrencePattern} but if other is also an
	 * EntryOccurrencePattern the test is much faster.
	 * 
	 * @param other {@inheritDoc}
	 * 
	 * @return {@inheritDoc}
	 */
	@Override
	public boolean equals(Object other)
	{
		if(!(other instanceof EntryOccurrencePattern))
			return super.equals(other);
		EntryOccurrencePattern op = (EntryOccurrencePattern)other;
		Iterator<OccurrencePatternEntry> i1 = this.entryIterator();
		Iterator<OccurrencePatternEntry> i2 = op.entryIterator();
		
		while(i1.hasNext() && i2.hasNext())
		{
			OccurrencePatternEntry e1 = i1.next();
			OccurrencePatternEntry e2 = i2.next();
			if(!(e1.getNumber() == e2.getNumber() && e1.getOccurrences() == e2.getOccurrences() ))
				return false;
		}
		if(i1.hasNext() || i2.hasNext())
			return false;
		return true;
	}
}
